Skip to content

fix: 2FA login always shows generic "bad credentials" error#9

Merged
ninocss merged 2 commits intomainfrom
copilot/fix-2fa-login-issue
Apr 11, 2026
Merged

fix: 2FA login always shows generic "bad credentials" error#9
ninocss merged 2 commits intomainfrom
copilot/fix-2fa-login-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 11, 2026

When a WebUntis server returns a generic error (e.g. "bad credentials") in response to an authenticate request that includes an OTP, the app showed the generic "Prüfe deine Daten" toast instead of the 2FA-specific "Code ungültig" error — leaving the user no indication that the OTP was the problem.

Root cause

_authenticateUntis only returned {otpInvalid: true} when the server's error message contained explicit OTP/2FA keywords ("otp", "2fa", "mfa", …). A generic "bad credentials" response fell through to return null, which _handleLogin maps to loginFailed.

Change

Simplified the invalidOtp condition in _authenticateUntis (lib/main.dart) to treat any server error as an invalid OTP when an OTP was provided:

// Before – only matches on specific keywords when OTP is present
(contains2faHint && otpCode != null && otpCode.isNotEmpty)

// After – any error while OTP is in flight = invalid OTP
(otpCode != null && otpCode.isNotEmpty)

This ensures the caller always receives {otpInvalid: true} in that path, keeping the 2FA input visible and surfacing the correct error message.

Copilot AI and others added 2 commits April 11, 2026 13:37
@ninocss ninocss marked this pull request as ready for review April 11, 2026 13:41
Copilot AI review requested due to automatic review settings April 11, 2026 13:41
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adjusts WebUntis 2FA error handling so OTP-related login failures don’t fall back to the generic “check your credentials” message when the server responds with a generic error.

Changes:

  • Updates _authenticateUntis to treat any JSON-RPC error as otpInvalid when an OTP was provided.
  • Adds an explanatory comment describing the intended UI behavior for 2FA failures.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/main.dart
Comment on lines +185 to 194
// Treat any server error as an invalid OTP when a code was provided, so
// the caller can show the 2FA-specific error instead of the generic
// "check your credentials" message.
final invalidOtp =
combined.contains('invalid otp') ||
combined.contains('invalid verification') ||
combined.contains('wrong otp') ||
combined.contains('otp invalid') ||
(contains2faHint && otpCode != null && otpCode.isNotEmpty);
(otpCode != null && otpCode.isNotEmpty);
if (invalidOtp) {
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invalidOtp now becomes true for any JSON-RPC error whenever a non-empty OTP was sent. This will classify unrelated server-side/auth errors (e.g., wrong username/password, invalid school/server, temporary backend failures) as “OTP invalid”, which can mislead users and mask actionable errors. Consider restricting this fallback to clearly auth-related failures (e.g., specific error codes/messages like "bad credentials") or returning a distinct flag so the UI can show a non-OTP error when appropriate.

Copilot uses AI. Check for mistakes.
Comment thread lib/main.dart
Comment on lines 188 to +193
final invalidOtp =
combined.contains('invalid otp') ||
combined.contains('invalid verification') ||
combined.contains('wrong otp') ||
combined.contains('otp invalid') ||
(contains2faHint && otpCode != null && otpCode.isNotEmpty);
(otpCode != null && otpCode.isNotEmpty);
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the new (otpCode != null && otpCode.isNotEmpty) clause, the preceding string checks in invalidOtp are redundant for the OTP-present path (the expression will always be true). If the intent is “any error when OTP is provided”, this can be simplified to improve readability and avoid implying that keyword matching still matters in that branch.

Copilot uses AI. Check for mistakes.
@ninocss ninocss merged commit 77440b5 into main Apr 11, 2026
8 checks passed
@ninocss ninocss deleted the copilot/fix-2fa-login-issue branch April 11, 2026 14:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants